import os
import uuid
import threading
import time
import logging
import os
from flask import Flask, request, jsonify
import requests
from moviepy.audio.io.AudioFileClip import AudioFileClip
import subprocess
import sys

# 导入处理器管理器和工具函数
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from video_processors import get_processor, list_available_processors
from utils.oss_utils import OSSUploader

# 导入配置
from config import config

# 导入记录管理器和VideoDisposeModel
from models.video_dispose_model import record_manager, VideoDisposeModel

# 初始化Flask应用
app = Flask(__name__)
# 从环境变量获取配置环境，默认为development
env = os.environ.get('FLASK_ENV', 'development')
app.config.from_object(config[env])

# 初始化配置
config[env].init_app(app)

# 配置日志
logging.basicConfig(
    level=getattr(logging, app.config['LOG_LEVEL']),
    handlers=[
        logging.FileHandler(app.config['LOG_FILE'], encoding='utf-8'),
        logging.StreamHandler(sys.stdout)
    ]
)
logger = logging.getLogger(__name__)

# 配置路径
base_dir = os.path.dirname(os.path.abspath(__file__))
temp_dir = os.path.join(base_dir, app.config['TEMP_DIR'])
output_dir = os.path.join(base_dir, app.config['OUTPUT_DIR'])

# 创建目录
os.makedirs(temp_dir, exist_ok=True)
os.makedirs(output_dir, exist_ok=True)

# 任务状态管理
processing_tasks = {}
callbacks = {}
retry_counts = {}
lock = threading.Lock()

@app.route('/', methods=['GET'])
def index():
    """默认页面，显示API文档"""
    # 获取所有可用的处理器
    available_processors = list_available_processors()
    
    # 使用普通字符串连接而不是f-string，避免反斜杠问题
    processors_html = '\n        '.join([f'<li><code>{processor}</code></li>' for processor in available_processors])
    
    return '''
    <h1>音视频处理服务</h1>
    <p>这是一个基于Python和MoviePy的音视频处理服务，提供了RESTful API接口来处理音视频文件。</p>
    
    <h2>可用的API接口:</h2>
    <ul>
        <li><strong>POST /process/video</strong> - 处理视频文件，根据指定的处理函数处理视频</li>
        <li><strong>POST /process/audio</strong> - 处理音频文件</li>
        <li><strong>GET /task/&lt;task_id&gt;</strong> - 查询任务状态</li>
    </ul>
    
    <h3>可用的视频处理函数:</h3>
    <ul>
        ''' + processors_html + '''
    </ul>
    
    <p>有关API的详细使用说明，请查看项目的README.md文件。</p>
    ''', 200, {'Content-Type': 'text/html; charset=utf-8'}

@app.route('/process/video', methods=['POST'])
def process_video():
    # 记录请求接收日志
    logger.info(f"收到视频处理请求: {request.remote_addr}, 方法: {request.method}")
    
    data = request.json
    callback_url = data.get('callback_url')
    process_func = data.get('process_func', 'default')  # 默认处理函数
    
    # 获取并打印JSON数据
    logger.info("视频处理请求JSON数据: {}".format(data))
    
    # 如果提供了回调URL，异步处理
    task_id = str(uuid.uuid4())
    
    # 存储回调URL
    callbacks[task_id] = callback_url
    
    # 记录异步任务创建日志
    logger.info(f"创建异步视频处理任务 - 任务ID: {task_id}, 处理函数: {process_func}, 回调URL: {callback_url}")
    
    # 启动处理进程，传递参数
    # 创建一个新的字典，包含所有参数
    task_params = request.json.copy()
    # 移除回调URL参数（特殊处理）
    if 'callback_url' in task_params:
        del task_params['callback_url']
    
    thread = threading.Thread(target=process_video_task, args=(task_id, process_func), kwargs=task_params)
    thread.start()
    
    return jsonify({"task_id": task_id, "status": "processing"})


@app.route('/task/<task_id>', methods=['GET'])
def get_task_status(task_id):
    # 直接从数据库查询任务状态
    task = record_manager.get_task(task_id)
    if task:
        return jsonify({
            "task_id": task_id,
            "status": task.status,
            "response": task.get_response_data()
        })
    else:
        return jsonify({"error": "Task not found"}), 404


def process_video_task(task_id, process_func_name, **kwargs):
    """根据指定的处理函数处理视频任务"""
    try:
        # 记录任务开始日志
        logger.info(f"开始视频任务处理 - 任务ID: {task_id}, 处理函数: {process_func_name}")
        
        # 创建任务并入库
        request_data = {
            'process_func': process_func_name,
            'params': kwargs
        }
        task = record_manager.create_task(request_data, task_id=task_id)
        logger.info(f"任务已创建并入库 - 任务ID: {task_id}")
        
        # 更新任务状态为处理中并保存到数据库
        task.status = VideoDisposeModel.Status.PROCESSING
        record_manager.update_task(task)
        
        # 初始化重试次数
        if task_id not in retry_counts:
            retry_counts[task_id] = 0
        
        # 使用处理器管理器获取处理函数
        process_func = get_processor(process_func_name)
        if not process_func:
            raise ValueError(f"处理函数 '{process_func_name}' 未找到")

        # 从参数中移除process_func和callback_url，避免传递给处理函数
        kwargs_copy = kwargs.copy()
        kwargs_copy.pop('process_func', None)
        kwargs_copy.pop('callback_url', None)

        # 添加task_id参数给处理函数
        kwargs_copy['task_id'] = task_id

        # 过滤掉处理函数不接受的参数
        import inspect
        # 获取处理函数的签名
        sig = inspect.signature(process_func)
        # 只保留处理函数接受的参数
        filtered_kwargs = {k: v for k, v in kwargs_copy.items() if k in sig.parameters}
        
        # 调用处理函数，传递过滤后的参数
        result = process_func(**filtered_kwargs)
        
        # 更新任务结果
        if task:
            task.status = VideoDisposeModel.Status.COMPLETED
            task.set_response_data(result)
            record_manager.update_task(task)
            logger.info(f"任务结果已更新 - 任务ID: {task_id}")
        
        # 记录任务完成日志
        logger.info(f"视频任务处理完成 - 任务ID: {task_id}, 结果: {result}")
        
        # 发送回调通知
        send_callback(task_id, "completed", task.get_response_data())
        
    except Exception as e:
        logger.error(f"视频任务处理错误 {task_id}: {str(e)}, 处理函数: {process_func_name}")
        # 确保任务ID在重试计数字典中
        if task_id not in retry_counts:
            retry_counts[task_id] = 0
        retry_counts[task_id] += 1
        
        # 检查是否需要重试
        if retry_counts[task_id] <= app.config['MAX_RETRIES']:
            logger.info(f"重试视频任务 {task_id} (尝试 {retry_counts[task_id]}/{app.config['MAX_RETRIES']}) - 处理函数: {process_func_name}")
            time.sleep(app.config['RETRY_DELAY'])
            process_video_task(task_id, process_func_name, **kwargs)
        else:
            processing_tasks[task_id] = "failed"
            logger.warning(f"视频任务处理失败 (达到最大重试次数) - 任务ID: {task_id}, 处理函数: {process_func_name}")
            
            # 更新任务状态为失败
            if task:
                task.status = VideoDisposeModel.Status.FAILED
                task.set_response_data({"error": str(e)})
                record_manager.update_task(task)
                logger.info(f"任务状态已更新为失败 - 任务ID: {task_id}")
            
            send_callback(task_id, "failed", task.get_response_data())



def download_file(url, filename):
    """下载文件并返回本地路径"""
    # 确保temp目录存在
    os.makedirs(app.config['TEMP_DIR'], exist_ok=True)
    
    file_path = os.path.join(app.config['TEMP_DIR'], filename)
    
    logger.info(f"开始下载文件 - URL: {url}, 目标路径: {file_path}")
    
    # 重试机制
    for attempt in range(app.config['MAX_RETRIES'] + 1):
        try:
            response = requests.get(url, timeout=app.config['REQUEST_TIMEOUT'])
            response.raise_for_status()
            
            with open(file_path, 'wb') as f:
                f.write(response.content)
            
            # 验证下载的文件
            file_size = os.path.getsize(file_path)
            if file_size < 1024:  # 文件大小小于1KB，可能是无效文件
                logger.warning(f"下载的文件太小，可能是无效文件: {file_path}, 大小: {file_size} 字节")
                # 尝试使用ffprobe检查文件格式（如果可用）
                try:
                    result = subprocess.run(
                        ['ffprobe', '-v', 'error', '-show_entries', 'format=format_name', '-of', 'default=noprint_wrappers=1:nokey=1', file_path],
                        capture_output=True, text=True, timeout=10
                    )
                    if result.returncode != 0:
                        logger.error(f"ffprobe验证失败，文件可能是无效的视频文件: {file_path}")
                        # 对于小文件，可以根据文件头判断是否为视频文件
                        with open(file_path, 'rb') as f:
                            header = f.read(12)
                            # 检查常见视频文件头特征
                            is_video = header.startswith(b'\x00\x00\x00\x18ftyp') or header.startswith(b'\xff\xd8') or header.startswith(b'GIF8') or header.startswith(b'BM')
                            if not is_video:
                                raise ValueError(f"下载的文件可能不是有效的视频文件: {file_path}")
                except (subprocess.SubprocessError, FileNotFoundError):
                    # 如果ffprobe不可用，跳过这个验证
                    logger.warning("ffprobe不可用，无法验证文件格式")
            
            logger.info(f"文件下载成功 - URL: {url}, 本地路径: {file_path}, 文件大小: {file_size} 字节")
            return file_path
        except Exception as e:
            logger.error(f"文件下载错误 {url} (尝试 {attempt + 1}/{app.config['MAX_RETRIES'] + 1}): {str(e)}")
            if attempt < app.config['MAX_RETRIES']:
                logger.info(f"{app.config['RETRY_DELAY']}秒后重试下载文件 {url}")
                time.sleep(app.config['RETRY_DELAY'])
            else:
                logger.error(f"文件下载失败 (达到最大重试次数) - URL: {url}")
                raise
    
    return None  # This should never be reached
def send_callback(task_id, status, result):
    """发送回调通知"""
    if task_id in callbacks and callbacks[task_id]:
        callback_url = callbacks[task_id]
        logger.info(f"准备发送回调通知 - 任务ID: {task_id}, 状态: {status}, 回调URL: {callback_url}")
        
        # 重试机制
        for attempt in range(app.config['MAX_RETRIES'] + 1):
            try:
                callback_data = {
                    "task_id": task_id,
                    "status": status,
                    "result": result
                }
                logger.debug(f"回调数据 - 任务ID: {task_id}, 数据: {callback_data}")
                
                response = requests.post(callback_url, json=callback_data, timeout=app.config['REQUEST_TIMEOUT'])
                response.raise_for_status()
                logger.info(f"回调通知发送成功 - 任务ID: {task_id}, 回调URL: {callback_url}, 响应状态码: {response.status_code}")
                return  # 成功发送回调后退出函数
            except Exception as e:
                logger.error(f"回调通知发送错误 {task_id} (尝试 {attempt + 1}/{app.config['MAX_RETRIES'] + 1}): {str(e)}")
                if attempt < app.config['MAX_RETRIES']:
                    logger.info(f"{app.config['RETRY_DELAY']}秒后重试发送回调 - 任务ID: {task_id}")
                    time.sleep(app.config['RETRY_DELAY'])
                else:
                    logger.error(f"回调通知发送失败 (达到最大重试次数) - 任务ID: {task_id}, 回调URL: {callback_url}")
    else:
        logger.warning(f"无法发送回调通知 - 任务ID: {task_id}, 回调URL不存在")


if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)